/**
**********************************************************************
* @file     main.c
* @brief    对文件的简要说明
* @details  对文件的详细说明
* @author   jcai
* @date     2022-05-05 15:53:13
* @copyright Copyright &copy; 2022 wy1107412334@163.com All rights reserved
* @version  v0.0.1
*
* \n Email:  wy1107412334@163.com
* \n Blog:   None
* \n Github: https://github.com/wy1107412334
* \n Gitee:  https://gitee.com/git.git1107412334.com
*
**********************************************************************
* @note
* None.
**********************************************************************
* @attention
* None.
**********************************************************************
@verbatim
@endverbatim
**********************************************************************
* @par 历史记录
* 1. v0.0.1: jcai, 2022-05-05 15:53:13
*   - 首次编写
*
*<center>Copyright &copy; 2022 wy1107412334@163.com All rights reserved</center>
**********************************************************************
*/

/* 头文件导入区 --------------------------------------------------- */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <SDL/SDL_video.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_events.h>
#include <SDL/SDL_timer.h>
#include <SDL/SDL_keysym.h>
#include <SDL/SDL_keyboard.h>


typedef struct
{
    int startTicks;
    int pausedTicks;
    int paused;
    int started;
} Timer_t;

typedef struct
{
    SDL_Rect box;
    int type;
} Tile_t;

typedef struct
{
    SDL_Rect box;
    int xVel, yVel;
} Dot_t;

// 窗口属性
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;

// 帧速率
const int FRAMES_PER_SECOND = 20;

// 圆点的大小
const int DOT_WIDTH = 20;
const int DOT_HEIGHT = 20;

// 画布大小
const int LEVEL_WIDTH = 1280;
const int LEVEL_HEIGHT = 960;

// 平铺常量
const int TILE_WIDTH = 80;
const int TILE_HEIGHT = 80;
const int TOTAL_TILES = 192;
#define TILE_SPRITES 12     //const int TILE_SPRITES = 12;
// 不同的贴图精灵
const int TILE_RED = 0;
const int TILE_GREEN = 1;
const int TILE_BLUE = 2;
const int TILE_CENTER = 3;
const int TILE_TOP = 4;
const int TILE_TOPRIGHT = 5;
const int TILE_RIGHT = 6;
const int TILE_BOTTOMRIGHT = 7;
const int TILE_BOTTOM = 8;
const int TILE_BOTTOMLEFT = 9;
const int TILE_LEFT = 10;
const int TILE_TOPLEFT = 11;

// 将要使用的表面
SDL_Surface *dot = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *tileSheet = NULL;
SDL_Surface *message= NULL;

// 事件结构体
SDL_Event event;

// 摄像机
SDL_Rect camera = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};

// 精灵图中的贴图表
SDL_Rect clips[TILE_SPRITES];

// 使用的字体
TTF_Font *font = NULL;
// 字体颜色
SDL_Color textColor = {0x00, 0x77, 0x77};


SDL_Surface *load_image(const char *file)
{
    SDL_Surface *loadedImage = NULL;
    SDL_Surface *optimizedImage = NULL;

    // 通过 SDL_imag 模块加载图片
    loadedImage = IMG_Load(file);
    if (!loadedImage)   return NULL;

    optimizedImage = SDL_DisplayFormat(loadedImage);
    SDL_FreeSurface(loadedImage);
    if (optimizedImage)
    {
        // 映射关键色
        uint32_t keycolor = SDL_MapRGB(optimizedImage->format, 0, 0xff, 0xff);
        // 将所有颜色为 RGB(0x00, 0xff, 0xff) 的像素设为透明
        SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, keycolor);
    }

    return optimizedImage;
}

void apply_surface(int x, int y, SDL_Surface *src, SDL_Surface *dst,
                   const SDL_Rect *clip)
{
    SDL_Rect offset;

    offset.x = x;
    offset.y = y;

    SDL_BlitSurface(src, (SDL_Rect *)clip, dst, &offset);
}

int init()
{
    if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
        return -1;

    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
                              SDL_SWSURFACE);
    if (!screen)
    {
        SDL_Quit();
        return -2;
    }

    if (TTF_Init() == -1)
    {
        SDL_Quit();
        return -3;
    }

    SDL_WM_SetCaption("Alpha Test", NULL);
    srand(SDL_GetTicks());

    return 0;
}

int load_files()
{
    // 加载图片
    dot = load_image("dot.png");
    if (!dot)
    {
        return -1;
    }

    tileSheet = load_image("tiles.png");
    if (!tileSheet)
    {
        SDL_FreeSurface(dot);
        return -2;
    }

    font = TTF_OpenFont("lazy.ttf", 32);
    if (!font)
    {
        SDL_FreeSurface(dot);
        SDL_FreeSurface(tileSheet);
        return -3;
    }

    return 0;
}

void clean_up(Tile_t *tiles[])
{
    int i;

    SDL_FreeSurface(dot);
    SDL_FreeSurface(tileSheet);
    if (message)    SDL_FreeSurface(message);

    for (i = 0; i < TOTAL_TILES; i++)
    {
        if (tiles[i])
        free(tiles[i]);
    }

    TTF_CloseFont(font);
    TTF_Quit();

    SDL_Quit();
}


void tile_init(Tile_t *tl, int x, int y, int tileType)
{
    tl->box.x = x;
    tl->box.y = y;
    tl->box.w = TILE_WIDTH;
    tl->box.h = TILE_HEIGHT;

    tl->type = tileType;
}
int check_collision(SDL_Rect A, SDL_Rect B)
{
    int leftA, leftB;
    int rightA, rightB;
    int topA, topB;
    int bottomA, bottomB;

    leftA = A.x;
    rightA = A.x + A.w;
    topA = A.y;
    bottomA = A.y + A.h;

    leftB = B.x;
    rightB = B.x + B.w;
    topB = B.y;
    bottomB = B.y + B.h;

    if (bottomA <= topB)    return 0;
    if (topA >= bottomB)    return 0;
    if (rightA <= leftB)    return 0;
    if (leftA >= rightB)    return 0;

    return 1;
}
void tile_show(const Tile_t *tl)
{
    if (check_collision(camera, tl->box))
        apply_surface(tl->box.x - camera.x, tl->box.y - camera.y,
                      tileSheet, screen, &clips[tl->type]);
}
int tile_get_type(const Tile_t *tl)
{
    return tl->type;
}
SDL_Rect tile_get_box(const Tile_t *tl)
{
    return tl->box;
}
int touches_wall(SDL_Rect box, const Tile_t *tiles[])
{
    int i;
    for (i = 0; i < TOTAL_TILES; i++)
    {
        if ((tile_get_type(tiles[i]) >= TILE_CENTER) &&
            (tile_get_type(tiles[i]) <= TILE_TOPLEFT))
        {
            if (check_collision(box, tile_get_box(tiles[i])))
                return 1;
        }
    }
    return 0;
}

void clip_titles()
{
    clips[TILE_RED].x = 0;
    clips[TILE_RED].y = 0;
    clips[TILE_RED].w = TILE_WIDTH;
    clips[TILE_RED].h = TILE_HEIGHT;

    clips[TILE_GREEN].x = 0;
    clips[TILE_GREEN].y = 80;
    clips[TILE_GREEN].w = TILE_WIDTH;
    clips[TILE_GREEN].h = TILE_HEIGHT;

    clips[TILE_BLUE].x = 0;
    clips[TILE_BLUE].y = 160;
    clips[TILE_BLUE].w = TILE_WIDTH;
    clips[TILE_BLUE].h = TILE_HEIGHT;

    clips[TILE_TOPLEFT].x = 80;
    clips[TILE_TOPLEFT].y = 0;
    clips[TILE_TOPLEFT].w = TILE_WIDTH;
    clips[TILE_TOPLEFT].h = TILE_HEIGHT;

    clips[TILE_LEFT].x = 80;
    clips[TILE_LEFT].y = 80;
    clips[TILE_LEFT].w = TILE_WIDTH;
    clips[TILE_LEFT].h = TILE_HEIGHT;

    clips[TILE_BOTTOMLEFT].x = 80;
    clips[TILE_BOTTOMLEFT].y = 160;
    clips[TILE_BOTTOMLEFT].w = TILE_WIDTH;
    clips[TILE_BOTTOMLEFT].h = TILE_HEIGHT;

    clips[TILE_TOP].x = 160;
    clips[TILE_TOP].y = 0;
    clips[TILE_TOP].w = TILE_WIDTH;
    clips[TILE_TOP].h = TILE_HEIGHT;

    clips[TILE_CENTER].x = 160;
    clips[TILE_CENTER].y = 80;
    clips[TILE_CENTER].w = TILE_WIDTH;
    clips[TILE_CENTER].h = TILE_HEIGHT;

    clips[TILE_BOTTOM].x = 160;
    clips[TILE_BOTTOM].y = 160;
    clips[TILE_BOTTOM].w = TILE_WIDTH;
    clips[TILE_BOTTOM].h = TILE_HEIGHT;

    clips[TILE_TOPRIGHT].x = 240;
    clips[TILE_TOPRIGHT].y = 0;
    clips[TILE_TOPRIGHT].w = TILE_WIDTH;
    clips[TILE_TOPRIGHT].h = TILE_HEIGHT;

    clips[TILE_RIGHT].x = 240;
    clips[TILE_RIGHT].y = 80;
    clips[TILE_RIGHT].w = TILE_WIDTH;
    clips[TILE_RIGHT].h = TILE_HEIGHT;

    clips[TILE_BOTTOMRIGHT].x = 240;
    clips[TILE_BOTTOMRIGHT].y = 160;
    clips[TILE_BOTTOMRIGHT].w = TILE_WIDTH;
    clips[TILE_BOTTOMRIGHT].h = TILE_HEIGHT;
}
int set_tiles(Tile_t *tiles[])
{
    FILE *pf;
    int x = 0, y = 0;
    int i, e;

    pf = fopen("lazy.map", "r");
    if (!pf)    return 1;

    for (i = 0; i < TOTAL_TILES; i++)
    {
        int ret;
        int tileType = -1;

        ret = fscanf(pf, "%d", &tileType);
        if (ret < 1)
        {
            e = -2;
            goto _error_0;
        }
        if ((tileType >= 0) && (tileType < TILE_SPRITES))
        {
            tiles[i] = (Tile_t *) malloc(sizeof(Tile_t));
            if (!tiles[i])
            {
                e = -3;
                goto _error_1;
            }

            tile_init(tiles[i], x, y, tileType);
        }
        else
        {
            e = -4;
            goto _error_1;
        }

        x += TILE_WIDTH;
        if (x >= LEVEL_WIDTH)
        {
            x = 0;
            y += TILE_HEIGHT;
        }
    }

    fclose(pf);
    return 0;

_error_1:
    for ( ; i > 0; i--)
    {
        free(tiles[i - 1]);
        tiles[i - 1] = NULL;
    }
_error_0:
    fclose(pf);
    return e;
}


void dot_init(Dot_t *dt)
{
    dt->box.x = 0;
    dt->box.y = 0;
    dt->box.w = DOT_WIDTH;
    dt->box.h = DOT_HEIGHT;

    dt->xVel = 0;
    dt->yVel = 0;
}
void dot_handle_input(Dot_t *dt)
{
    if (event.type == SDL_KEYDOWN)
    {
        switch (event.key.keysym.sym)
        {
        case SDLK_UP: dt->yVel -= DOT_HEIGHT / 2; break;
        case SDLK_DOWN: dt->yVel += DOT_HEIGHT / 2; break;
        case SDLK_LEFT: dt->xVel -= DOT_WIDTH / 2; break;
        case SDLK_RIGHT: dt->xVel += DOT_WIDTH / 2; break;
        default: break;
        }
    }
    else if (event.type == SDL_KEYUP)
    {
        switch (event.key.keysym.sym)
        {
        case SDLK_UP: dt->yVel += DOT_HEIGHT / 2; break;
        case SDLK_DOWN: dt->yVel -= DOT_HEIGHT / 2; break;
        case SDLK_LEFT: dt->xVel += DOT_WIDTH / 2; break;
        case SDLK_RIGHT: dt->xVel -= DOT_WIDTH / 2; break;
        default: break;
        }
    }
}
void dot_move(Dot_t *dt, Tile_t *tiles[])
{
    dt->box.x += dt->xVel;
    if ((dt->box.x < 0) || ((dt->box.x + DOT_WIDTH) > LEVEL_WIDTH) ||
        touches_wall(dt->box, (const Tile_t **)tiles))
        dt->box.x -= dt->xVel;

    dt->box.y += dt->yVel;
    if ((dt->box.y < 0) || ((dt->box.y + DOT_HEIGHT) > LEVEL_HEIGHT) ||
        touches_wall(dt->box, (const Tile_t **)tiles))
        dt->box.y -= dt->yVel;
}
void dot_show(const Dot_t *dt)
{
    apply_surface(dt->box.x - camera.x, dt->box.y - camera.y,
                  dot, screen, NULL);
}
void dot_set_camera(Dot_t *dt)
{
    camera.x = (dt->box.x + DOT_WIDTH / 2) - SCREEN_WIDTH / 2;
    camera.y = (dt->box.y + DOT_HEIGHT / 2) - SCREEN_HEIGHT / 2;

    if (camera.x < 0)
        camera.x = 0;
    if (camera.y < 0)
        camera.y = 0;

    if (camera.x > (LEVEL_WIDTH - camera.w))
        camera.x = LEVEL_WIDTH - camera.w;
    if (camera.y > (LEVEL_HEIGHT - camera.h))
        camera.y = LEVEL_HEIGHT - camera.h;
}


void timer_init(Timer_t *tm)
{
    tm->startTicks = 0;
    tm->pausedTicks = 0;
    tm->paused = 0;
    tm->started = 0;
}
void timer_start(Timer_t *tm)
{
    tm->started = 1;
    tm->paused = 0;

    tm->startTicks = SDL_GetTicks();
}
void timer_stop(Timer_t *tm)
{
    tm->started = 0;
}
void timer_pause(Timer_t *tm)
{
    if (tm->started && !tm->paused)
    {
        tm->paused = 1;
        tm->pausedTicks = SDL_GetTicks() - tm->startTicks;
    }
}
void timer_unpause(Timer_t *tm)
{
    if (tm->paused)
    {
        tm->paused = 0;

        tm->startTicks = SDL_GetTicks() - tm->pausedTicks;
        tm->pausedTicks = 0;
    }
}
int timer_get_ticks(Timer_t *tm)
{
    if (tm->started)
    {
        if (tm->paused)
            return tm->pausedTicks;
        else
            return SDL_GetTicks() - tm->startTicks;
    }
    return 0;
}
int timer_is_started(Timer_t *tm)
{
    return tm->started;
}
int timer_is_paused(Timer_t *tm)
{
    return tm->paused;
}


/**
**********************************************************************
* @brief    简要说明
* @details  详细说明
* @param    none
* @param[in]     a: 该参数用于输入
* @param[in,out] b: 该参数用于输入和输出
* @param[out]    c: 该参数用于输出
* @retval  none
* @retval  0 函数成功
* @retval  1 函数失败
* @return  返回0成功；否则失败
* @note    none
*/
int main(int argc, char *argv[])
{
    Dot_t dt;
    Timer_t fps;
    Tile_t *tiles[TOTAL_TILES];
    int quit = 0;
    int i;

    if (init() != 0)
    {
        fprintf(stderr, "SDL初始化失败\n");
        return EXIT_FAILURE;
    }

    if (load_files() != 0)
    {
        fprintf(stderr, "SDL加载 background.png 失败\n");
        return EXIT_FAILURE;
    }

    dot_init(&dt);
    timer_init(&fps);
    clip_titles();
    if (set_tiles(tiles))
    {
        fprintf(stderr, "精灵图贴图表初始化失败\n");
        return EXIT_FAILURE;
    }

    message = TTF_RenderText_Solid(font, "Press any Arrow keys.",
                                   textColor);

    while (!quit)
    {
        timer_start(&fps);

        // 处理SDL的事件
        while (SDL_PollEvent(&event))
        {
            dot_handle_input(&dt);

            // 如果用户点击了窗口右上角的关闭按钮
            if (event.type == SDL_QUIT)
            {
                quit = 1;
                printf("处理退出事件\n");
            }
        }

        // 更新背景
        SDL_FillRect(screen, &screen->clip_rect,
                     SDL_MapRGB(screen->format, 0xff, 0xff, 0xff));

        dot_move(&dt, tiles);
        dot_set_camera(&dt);
        for (i = 0; i < TOTAL_TILES; i++)
        {
            tile_show(tiles[i]);
        }
        if (message)
            apply_surface(dt.box.x - camera.x + dt.box.w,
                          dt.box.y - camera.y,
                          message, screen, NULL);
        dot_show(&dt);

        // 更新窗口
        if (SDL_Flip(screen) == -1)
        {
            fprintf(stderr, "SDL更新窗口失败\n");
            break;
        }

        // 控制帧速率
        if (timer_get_ticks(&fps) < (1000 / FRAMES_PER_SECOND))
            SDL_Delay((1000 / FRAMES_PER_SECOND) - timer_get_ticks(&fps));
    }

    clean_up(tiles);

    return 0;
}


/***  Copyright (C) 2022 wy1107412334@163.com All rights reserved ***/
/****************************** END OF FILE *************************/

